package com.gitee.huanminabc.multimedia.ffmpeg;

import com.gitee.huanminabc.jcommon.file.FilePathPartEnum;
import com.gitee.huanminabc.jcommon.file.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Slf4j
public class FfmpegInfoUtil {

    //获取音频是单声道还是双声道 ,1=单声道, 2=立体声 ,返回true=立体声,false=单声道
    public static boolean getAudioChannel(String audioPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(audioPath)).getInfo();
            return multimediaInfo.getAudio().getChannels() == 2;
        } catch (EncoderException e) {
            log.error("获取音频声道异常", e);
        }
        return false;
    }

    /**
     * 通过本地路径获取多媒体文件信息(宽，高，时长，编码等) ,支持视频和音频和图片
     *
     * @param filePath 文件路径
     * @return MultimediaInfo 媒体对象,包含 (宽，高，时长，编码等)
     */
    public static MultimediaInfo GetMediaInfo(String filePath) {
        try {
            return new MultimediaObject(new File(filePath)).getInfo();
        } catch (EncoderException e) {
            log.error("获取媒体信息异常!,error:{}", e.getMessage());
        }
        return null;
    }

    //获取视频编码格式, h264,h265等
    public static String getVideoCodec(String videoPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(videoPath)).getInfo();
            String decoder = multimediaInfo.getVideo().getDecoder();
            if (decoder.contains("h264") || decoder.contains("avc")) {
                return "h264";
            } else if (decoder.contains("h265") || decoder.contains("hevc")) {
                return "h265";
            } else if (decoder.contains("vp9") || decoder.contains("vp09")) {
                return "vp9";
            } else if (decoder.contains("vp8") || decoder.contains("vp08")) {
                return "vp8";
            } else if (decoder.contains("av1") || decoder.contains("av01")) {
                return "av1";
            }
        } catch (EncoderException e) {
            log.error("获取视频编码格式异常,error:{}", e.getMessage());
        }
        throw new RuntimeException("获取视频编码格式异常,没有找到这个视频的编码:" + videoPath);
    }
    //将视频编码转换为ffmpeg支持的编码格式
    public static String getVideoCodecToFFmpeg(String videoPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(videoPath)).getInfo();
            String decoder = multimediaInfo.getVideo().getDecoder();
            if (decoder.contains("h264") || decoder.contains("avc")) {
                return "libx264";
            } else if (decoder.contains("h265") || decoder.contains("hevc")) {
                return "libx265";
            } else if (decoder.contains("vp9") || decoder.contains("vp09")) {
                return "libvpx-vp9";
            } else if (decoder.contains("vp8") || decoder.contains("vp08")) {
                return "libvpx";
            } else if (decoder.contains("av1") || decoder.contains("av01")) {
                return "libaom-av1";
            }
        } catch (EncoderException e) {
            log.error("获取视频编码格式异常,error:{}", e.getMessage());
        }
        throw new RuntimeException("获取视频编码格式异常,没有找到这个视频的编码:" + videoPath);
    }

    //获取视频的帧率
    public static double getVideoFrameRate(String videoPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(videoPath)).getInfo();
            return multimediaInfo.getVideo().getFrameRate();
        } catch (EncoderException e) {
            log.error("获取视频帧率异常", e);
        }
        return 0;
    }
    //获取音频的比特率
    public static double getAudioFrameRate(String audioPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(audioPath)).getInfo();
            return multimediaInfo.getAudio().getBitRate();
        } catch (EncoderException e) {
            log.error("获取音频帧率异常", e);
        }
        return 0;
    }

    //获取音频或视频的时间 单位秒
    public static int getTime(String videoPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(videoPath)).getInfo();
            return (int)multimediaInfo.getDuration()/1000;
        } catch (EncoderException e) {
            log.error("获取视频时间异常", e);
        }
        return 0;
    }
    //获取视频是否包含音频
    public static boolean getVideoHasAudio(String videoPath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(videoPath)).getInfo();
            return multimediaInfo.getAudio()!= null;
        } catch (EncoderException e) {
            log.error("获取视频是否包含音频异常", e);
        }
        return false;
    }


    //获取图片的宽和高 ,支持视频和图片
    public static int[] GeWidthAndHeight(String imagePath) {
        try {
            MultimediaInfo multimediaInfo = new MultimediaObject(new File(imagePath)).getInfo();
            return new int[]{multimediaInfo.getVideo().getSize().getWidth(), multimediaInfo.getVideo().getSize().getHeight()};
        } catch (EncoderException e) {
            log.error("获取宽高异常", e);
        }
        return null;
    }


    public static void ExecuteCmd(List<String> cmds) {
        ProcessWrapper ffmpeg = null;
        try {
            ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            for (String cmd : cmds) {
                ffmpeg.addArgument(cmd);
            }
            ffmpeg.execute();
            //等待完成
            WaitFfmpegFinish(ffmpeg);

        } catch (Exception e) {
            throw  new RuntimeException( e.getMessage());
        } finally {
            if (ffmpeg != null) {
                ffmpeg.destroy();
            }
        }
    }

    /**
     * 等待命令执行完成
     *
     * @return 成功返回null, 否则返回错误信息
     */
    public static String WaitFfmpegFinish(ProcessWrapper processWrapper){
        //存储执行错误信息
        StringBuilder errorSb = new StringBuilder();
        //ffmpeg的执行过程打印在errorStream中
        try (
                //获取错误流
                BufferedReader br = new BufferedReader(new InputStreamReader(processWrapper.getErrorStream()));

        ) {
            String processInfo;
            while ((processInfo = br.readLine()) != null) {
                errorSb.append(processInfo).append("\r\n");
            }
            //执行完成后,获取处理退出编码
            int processExitCode = processWrapper.getProcessExitCode();
            if (processExitCode != 0) {//非0执行失败,返回执行过程信息
                if (StringUtils.hasLength(errorSb)) {
                    throw new RuntimeException("执行命令失败error:" + errorSb);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return null;
    }

    //获取正确和错误流转换为map
    public static List<String> ExecuteCmdReturn(List<String> cmds) {
        ProcessWrapper ffmpeg = null;
        try {
            ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            for (String cmd : cmds) {
                ffmpeg.addArgument(cmd);
            }
            ffmpeg.execute();


            List<String> error = readStream(ffmpeg.getErrorStream());
            List<String> info = readStream(ffmpeg.getInputStream());
            //等待完成, 不管是否成功,都等待完成都放在一起返回, 交给调用者处理
            int processExitCode = ffmpeg.getProcessExitCode();
            error.addAll(info);
            return error;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (ffmpeg != null) {
                ffmpeg.destroy();
            }
        }
    }



    //打印错误信息
    private static void printError(List<String> error) {
       StringBuilder errorSb = new StringBuilder();
        for (String s : error) {
            errorSb.append(s).append("\r\n");
        }
        if (errorSb.length() > 0){
            throw new RuntimeException("执行命令失败error:" + errorSb);
        }
    }

    //获取命令执行结果
    private static List<String> readStream(InputStream inputStream) throws IOException {
        List<String> result = new ArrayList<>();
        try (
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {

            String line;
            while ((line = reader.readLine()) != null) {
                result.add(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;

    }


    //获取输出路径的缓存路径
    public static String getOutputCachePath(String outputPath,String cacheFileName) {
        //获取输出视频目录
        String currentDirectoryOut = FileUtil.getFilePathPartInfo(outputPath, FilePathPartEnum.currentDirectory);
        //获取输出视频文件名
         cacheFileName= UUID.randomUUID()+ "_cache_"+cacheFileName;
         //拼接
        return currentDirectoryOut + File.separator + cacheFileName;
    }

}
